package org.example.zuul.filter.pre;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.example.zuul.filter.ZuulFilter;
import org.example.zuul.filter.ZuulProperties;
import org.example.zuul.http.RequestContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.RequestEntity;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.MultiValueMap;
import org.springframework.util.PathMatcher;
import org.springframework.util.StreamUtils;

public class RequestWrapperFilter implements ZuulFilter {
    private static final Logger LOGGER = LoggerFactory.getLogger(RequestWrapperFilter.class);
    private final PathMatcher pathMatcher = new AntPathMatcher();
    private final List<ZuulProperties.ZuulRoute> routes;

    public RequestWrapperFilter(Collection<ZuulProperties.ZuulRoute> routes) {
        this.routes = new ArrayList<>(routes);
    }

    @Override
    public void run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        String uri = request.getRequestURI();
        for (ZuulProperties.ZuulRoute route : routes) {
            if (pathMatcher.match(route.getPath(), uri)) {
                uri = route.getUrl() + '/' + pathMatcher.extractPathWithinPattern(route.getPath(), uri);
                break;
            }
        }
        try {
            RequestEntity<byte[]> requestEntity = createRequestEntity(request, uri);
            //将请求体requestEntity放置全局对象当中
            ctx.setRequestEntity(requestEntity);
        } catch (Exception e) {
            LOGGER.error("create request exception", e);
        }
    }

    @Override
    public int filterOrder() {
        return -1;
    }

    @Override
    public String filterType() {
        return ZuulFilter.TYPE_PRE;
    }

    public void addRouters(Collection<ZuulProperties.ZuulRoute> routes) {
        this.routes.addAll(routes);
    }

    private RequestEntity<byte[]> createRequestEntity(HttpServletRequest request, String url) throws URISyntaxException, IOException {
        String method = request.getMethod();
        HttpMethod httpMethod = HttpMethod.resolve(method);
        //1.封装请求头
        MultiValueMap<String, String> headers = createRequestHeaders(request);
        //2.封装请求体
        byte[] body = createRequestBody(request);
        //3.构造出RestTemplate能识别的RequestEntity
        return new RequestEntity<>(body, headers, httpMethod, new URI(url));
    }

    private MultiValueMap<String, String> createRequestHeaders(HttpServletRequest request) {
        HttpHeaders headers = new HttpHeaders();
        for (String headerName : Collections.list(request.getHeaderNames())) {
            for (String headerValue : Collections.list(request.getHeaders(headerName))) {
                headers.add(headerName, headerValue);
            }
        }
        return headers;
    }

    private byte[] createRequestBody(HttpServletRequest request) throws IOException {
        return StreamUtils.copyToByteArray(request.getInputStream());
    }
}
